package com.jinsi.util;


import cn.hutool.core.util.IdUtil;
import com.coremedia.iso.IsoFile;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bytedeco.ffmpeg.avcodec.AVCodecParameters;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.avformat.AVStream;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.*;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
 * @author hy
 * @date 2022-06-13 上午 10:38
 * 视频编译格式更改配置类
 */
@Slf4j


public class VideoUtils {
    /**
     * 视频转分辨率转视频编码
     *
     * @param inputFile   源文件
     * @param outputPath  输出目录
     * @param width       需要转成的视频的宽度
     * @param height      需要转成的视频的高度
     * @param videoFormat 需要转换成的视频格式
     * @return 返回新文件名称，可以根据实际使用修改
     */
    public static String videoConvert(String inputFile, String outputPath, Integer width, Integer height, String videoFormat, HttpServletRequest request) {



        List<String> paths = Splitter.on(".").splitToList(inputFile);
        String ext = paths.get(paths.size() - 1);
        if (StringUtils.isEmpty(videoFormat)) {
            videoFormat = ext;
        }
        String newFileName = IdUtil.simpleUUID() + "." + videoFormat;
        String resultPath =
                Joiner.on(File.separator).join(Arrays.asList(outputPath, newFileName));
        FFmpegLogCallback.set();
        Frame frame;
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile);
        System.out.println("grabber:"+grabber);
        FFmpegFrameRecorder recorder = null;
        try {
            // 初始化帧抓取器，例如数据结构（时间戳、编码器上下文、帧对象等），
            // 如果入参等于true，还会调用avformat_find_stream_info方法获取流的信息，放入AVFormatContext类型的成员变量oc中
            grabber.start(true);
            // grabber.start方法中，初始化的解码器信息存在放在grabber的成员变量oc中
            AVFormatContext avformatcontext = grabber.getFormatContext();
            // 文件内有几个媒体流（一般是视频流+音频流）
            int streamNum = avformatcontext.nb_streams();
            // 没有媒体流就不用继续了
            if (streamNum < 1) {
                log.info("视频文件格式不对");
                return "error";
            }
            // 取得视频的帧率
            int framerate = (int) grabber.getVideoFrameRate();
            log.info("视频帧率:{}，视频时长:{}秒，媒体流数量:{}", framerate, avformatcontext.duration() / 1000000,
                    streamNum);
            // 遍历每一个流，检查其类型
            for (int i = 0; i < streamNum; i++) {
                AVStream avstream = avformatcontext.streams(i);
                AVCodecParameters avcodecparameters = avstream.codecpar();
                log.info("流的索引:{}，编码器类型:{}，编码器ID:{}", i, avcodecparameters.codec_type(),
                        avcodecparameters.codec_id());
            }
            // 源视频宽度
            int frameWidth = grabber.getImageWidth();
            // 源视频高度
            int frameHeight = grabber.getImageHeight();
            // 源音频通道数量
            int audioChannels = grabber.getAudioChannels();
            log.info("源视频宽度:{}，源视频高度:{}，音频通道数:{}", frameWidth, frameHeight, audioChannels);
            recorder = new FFmpegFrameRecorder(resultPath, width, height, audioChannels);
            // 设置H264编码
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            // 设置视频格式
            recorder.setFormat(videoFormat);
            // 使用原始视频的码率，若需要则自行修改码率
            recorder.setVideoBitrate(2000);
            // 一秒内的帧数，帧率
            recorder.setFrameRate(framerate);
            // 两个关键帧之间的帧数
            recorder.setGopSize(framerate);
            // 设置音频通道数，与视频源的通道数相等
            recorder.setAudioChannels(grabber.getAudioChannels());
            recorder.start();
            int videoFrameNum = 0;
            int audioFrameNum = 0;
            int dataFrameNum = 0;
            double i = 0;
            double a =  grabber.getLengthInAudioFrames();
            // 持续从视频源取帧
            while (null != (frame = grabber.grab())) {
                i++;
                double c = 25.0;
                c += Math.round(i/a*100*0.5);
                if(c < 75){
                    request.getSession().setAttribute("progress",c);
                }

                // 有图像，就把视频帧加一
                if (null != frame.image) {
                    videoFrameNum++;
                    // 取出的每一帧，都保存到视频
                    recorder.record(frame);
                }
                // 有声音，就把音频帧加一
                if (null != frame.samples) {
                    audioFrameNum++;
                    // 取出的每一帧，都保存到视频
                    recorder.record(frame);
                }
                // 有数据，就把数据帧加一
                if (null != frame.data) {
                    dataFrameNum++;
                }
            }
            log.info("转码完成，视频帧:{}，音频帧:{}，数据帧:{}", videoFrameNum, audioFrameNum, dataFrameNum);
        } catch (Exception e) {
            log.error(e.getMessage());
            return "error";
        } finally {
            if (recorder != null) {
                try {
                    recorder.close();
                } catch (Exception e) {
                    log.info("recorder.close异常" + e);
                }
            }
            try {
                grabber.close();
            } catch (FrameGrabber.Exception e) {
                log.info("frameGrabber.close异常" + e);
            }
        }
        return newFileName;
    }
    /**
     * 获取视频文件的播放长度(mp4、mov格式)
     * @param videoPath
     * @return 单位为毫秒
     */
    public static long getMp4Duration(String videoPath) throws IOException {
        IsoFile isoFile = new IsoFile(videoPath);
        long lengthInSeconds =
                isoFile.getMovieBox().getMovieHeaderBox().getDuration() /
                        isoFile.getMovieBox().getMovieHeaderBox().getTimescale();
        return lengthInSeconds;
    }


    /**
     * 得到语音或视频文件时长,单位秒
     * @param filePath
     * @return
     * @throws IOException
     */
    public static long getDuration(String filePath) throws IOException {
        String format = getVideoFormat(filePath);
        long result = 0;
        if("wav".equals(format)){
            result = AudioUtil.getDuration(filePath).intValue();
        }else if("mp3".equals(format)){
            result = AudioUtil.getMp3Duration(filePath).intValue();
        }else if("m4a".equals(format)) {
            result = VideoUtils.getMp4Duration(filePath);
        }else if("mov".equals(format)){
            result = VideoUtils.getMp4Duration(filePath);
        }else if("mp4".equals(format)){
            result = VideoUtils.getMp4Duration(filePath);
        }

        return result;
    }

    /**
     * 得到语音或视频文件时长,单位秒
     * @param filePath
     * @return
     * @throws IOException
     */
    public static long getDuration(String filePath,String format) throws IOException {
        long result = 0;
        if("wav".equals(format)){
            result = AudioUtil.getDuration(filePath).intValue();
        }else if("mp3".equals(format)){
            result = AudioUtil.getMp3Duration(filePath).intValue();
        }else if("m4a".equals(format)) {
            result = VideoUtils.getMp4Duration(filePath);
        }else if("mov".equals(format)){
            result = VideoUtils.getMp4Duration(filePath);
        }else if("mp4".equals(format)){
            result = VideoUtils.getMp4Duration(filePath);
        }

        return result;
    }


    /**
     * 得到文件格式
     * @param path
     * @return
     */
    public static String getVideoFormat(String path){
        return  path.toLowerCase().substring(path.toLowerCase().lastIndexOf(".") + 1);
    }
}
